﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Bling.Core;
using Bling.WPF;
using Bling.Util;
using System.Windows.Input;
using Bling.Shaders;
using Bling.Graphics;
using Bling.WPF3D;

namespace Bling.Tutorial {
  // Demonstrate how Bling's web browser can be used.
  public class BrowserTest : Canvas, ICanStart {
    public static readonly ImageSource NormalImage = "Resources/4573-normal.jpg".ImageFor(typeof(BrowserTest));
    public static readonly ImageSource HeightImage = "Resources/4573-normal_DISP.jpg".ImageFor(typeof(BrowserTest));
    private Bling.Browser.Page Page;

    public void Start() { if (Page != null) Page.Start();  }
    public void Stop() { if (Page != null) Page.Stop();  }

    public BrowserTest() {
      CanvasBl canvas = this;
      canvas.Background = Colors.LightGray;
      // Create a text box to hold the URL. Note that we don't do any URL cleaning so entering a non-URL will crash the app.
      TextBoxBl uriBox = new TextBoxBl(canvas);
      uriBox.LeftTop.Bind = new PointBl(0d, 0d);
      uriBox.Width.Bind = canvas.Width;
      uriBox.Text = "http://bling.codeplex.com";
      // A slider that will hold the height of our light, below the uriBox.
      LabelSliderBl HeightOfLight = new LabelSliderBl(canvas) {
        LeftTop = uriBox.LeftBottom,
        LabelName = "LightHeight",
        WidthRatio = 0.14,
        Minimum = 80,
        Maximum = 800,
        Value = 450,
      };
      // A canvas to hold the content of the web browser.
      CanvasBl content = new CanvasBl(canvas) {
        LeftTop = HeightOfLight.LeftBottom,
        Height = canvas.Height - HeightOfLight.Bottom,
        ClipToBounds = true,
      };
      // A scroll bar for scrolling the web browser.
      ScrollBarBl scrollBar = new ScrollBarBl(canvas) {
        Height = content.Height,
        Top = content.Top,
        Orientation = Orientation.Vertical,
        Minimum = 0,
        Maximum = 1,
      };
     
      scrollBar.Left = content.Right.Min(canvas.Width - 2d * scrollBar.Width);
      // remember to subtract out the scroll bar's width.
      content.Width = canvas.Width - scrollBar.Width;
      Action<double> SetContentWidth = content.Width.MakeAssign;
      ImageBl image = new ImageBl(content) {
        Stretch = Stretch.Fill,
        // scroll bar will move image vertically within content canvas.
        LeftTop = new PointBl(0d, -(scrollBar.Value * (3000 - content.Height))),
        Size = new PointBl(1000, 3000),
      };
      // we must wait until the image is loaded to create the web browser page.
      image.CurrentValue.Loaded += (x, y) => {
        // create the web browser page with respect to image and a fixed size.
        // refresh the page every 100 milliseconds to show flash and such.
        Page = new Bling.Browser.Page(image, new Point(1000, 3000), 100);
        // The output of the browser is written to the page's Bitmap, so use that as the image source
        image.Source = Page.Bitmap;
        // navigate initially to our start page.
        Page.Navigate(new Uri(uriBox.Text.CurrentValue));
        Page.DownloadStart += (x0, url) => {
          // rewrite the url in the url box if necessary.
          uriBox.Text = url.ToString();
          // disable the url box while downloading.
          uriBox.IsEnabled = false;
        };
        Page.DownloadCompleted += (x0, width) => {
          // re-enable the download box.
          uriBox.IsEnabled = true;
          SetContentWidth(width);
        };
        // when we press enter in the uriBox, navigate.
        uriBox.CurrentValue.KeyDown += (x0, y0) => {
          if (y0.Key == Key.Enter)
            Page.Navigate(new Uri(uriBox.Text.CurrentValue));
        };
        // forward mouse clicks on.
        image.CurrentValue.MouseDown += (image0, x0) => {
          Page.DoClick(x0.GetPosition((Image)image0));
        };
      };
      // gratiutous normal mapping!

      // a dumb thumb to trace a 2D position. 
      ThumbBl LightXY = new ThumbBl();
      {
        // create an action that updates the thumb's position quickly without data binding
        Action UpdateLightXY = PointBl.Assign(LightXY, canvas.MousePosition);
        // update on mouse move within the entire canvas.
        this.MouseMove += (xx, yy) => UpdateLightXY();
      }
      // a point light from WPF 3D will act as our light source.
      // combine point light with diffuse matterial to create lighting effect.
      Lighting lighting = (new DiffuseMaterialCl()).ApplyLight(new PointLightBl() {
        Position = new Point3DBl(LightXY, HeightOfLight),
        Color = Colors.White,
      });
      // create a pixel effect from lighting using a height field and normal maps loaded from textures 4573*.jpg
      // this is basically normal/bump mapping.
      Tex2D HeightField = new ImageBrushBl(HeightImage);
      Tex2D NormalMap = new ImageBrushBl(NormalImage);
      // make it scroll!
      HeightField = HeightField.Distort(uv => new PointBl(uv.X, (uv.Y + scrollBar.Value).Frac));
      NormalMap =     NormalMap.Distort(uv => new PointBl(uv.X, (uv.Y + scrollBar.Value).Frac));

      // apply lighting to canvas.
      canvas.Effect.Custom = lighting.Apply((HeightField)HeightField, (NormalMap)NormalMap, 
                                    canvas.Size.InsertZ(0.015d), canvas.CenterSize.InsertZ(100d));
    }
  }
}